﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Transactions;
using System.Collections.ObjectModel;

namespace ScrumLite.Data.Tests
{
    /// <summary>
    /// Summary description for ProjectManagerFixture
    /// </summary>
    [TestClass]
    public class ProjectManagerFixture
    {
        IProjectStore store;
        private static Guid projectId = new Guid("1C4F9968-8D5E-4d8a-9BD8-9A8DF1434B5A");
        private static Guid pp1 = new Guid("112F9968-8D5E-4d8a-9BD8-9A8DF2342B5A");
        private static Guid pp2 = new Guid("433F9968-8D5E-4d8a-9BD8-9A8343534B5A");
        private static Guid r1Id = new Guid("433F9968-8D5E-4d8a-9BD8-9A8343534B5A");
        private const string projectName = "FakeProject";

        [TestInitialize]
        public void Initialize()
        {
            this.store = new ProjectStore();
        }

        [TestMethod()]
        public void ShouldGetProject()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                CreateFakeProjectStore();

                Project actual = this.store.GetProject(projectId);

                Assert.IsNotNull(actual);
                Assert.AreEqual(DateTime.Today, actual.endsOn);
                Assert.AreEqual(projectId, actual.id);
                Assert.AreEqual("FakeProject", actual.name);
                Assert.AreEqual("fakeGroup", actual.security_group);
                Assert.AreEqual(DateTime.Today, actual.startsOn);
                Assert.AreEqual("www.fakeUri.com", actual.uri_repository);
                Assert.AreEqual("fakeUser", actual.user_ownedBy);
                Assert.AreEqual("www.fakeUriMetrics.com", actual.uri_projectSpecificMetrics);
            }
        }

        [TestMethod()]
        public void ShouldGetProjectByName()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                CreateFakeProjectStore();

                Project actual = this.store.GetProjectByName(projectName);

                Assert.IsNotNull(actual);
                Assert.AreEqual(DateTime.Today, actual.endsOn);
                Assert.AreEqual(projectId, actual.id);
                Assert.AreEqual(projectName, actual.name);
                Assert.AreEqual("fakeGroup", actual.security_group);
                Assert.AreEqual(DateTime.Today, actual.startsOn);
                Assert.AreEqual("www.fakeUri.com", actual.uri_repository);
                Assert.AreEqual("fakeUser", actual.user_ownedBy);
                Assert.AreEqual("www.fakeUriMetrics.com", actual.uri_projectSpecificMetrics);
            }
        }

        [TestMethod()]
        public void ShouldGetProjects()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                CreateFakeProjectStore();

                Project[] actual = this.store.GetProjects();
                
                Assert.IsNotNull(actual);
                Assert.IsTrue(actual.Contains(actual.FirstOrDefault(p => p.id == projectId)));
            }
        }

        [TestMethod()]
        public void ShouldAddProject()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                Guid newProjectId = Guid.NewGuid();
                Project project = new Project() { id = newProjectId, user_ownedBy = "test", uri_repository = "www.test", startsOn = DateTime.Today, security_group = "test", name = "test", endsOn = DateTime.Today, uri_projectSpecificMetrics = "www.testMetrics" };

                this.store.Add(project);

                Project actual = this.store.GetProject(newProjectId);

                Assert.IsNotNull(actual);
                Assert.AreEqual(project.endsOn, actual.endsOn);
                Assert.AreEqual(project.id, actual.id);
                Assert.AreEqual(project.name, actual.name);
                Assert.AreEqual(project.security_group, actual.security_group);
                Assert.AreEqual(project.startsOn, actual.startsOn);
                Assert.AreEqual(project.uri_repository, actual.uri_repository);
                Assert.AreEqual(project.user_ownedBy, actual.user_ownedBy);
                Assert.AreEqual(project.uri_projectSpecificMetrics, actual.uri_projectSpecificMetrics);
            }
        }

        [TestMethod()]
        public void ShouldUpdateProject()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                CreateFakeProjectStore();

                Project project = this.store.GetProject(projectId);
                project.name = "ProjectUpdated";

                this.store.Update(project);

                Project actual = this.store.GetProject(projectId);

                Assert.IsNotNull(actual);
                Assert.AreEqual(project.endsOn, actual.endsOn);
                Assert.AreEqual(project.id, actual.id);
                Assert.AreEqual("ProjectUpdated", actual.name);
                Assert.AreEqual(project.security_group, actual.security_group);
                Assert.AreEqual(project.startsOn, actual.startsOn);
                Assert.AreEqual(project.uri_repository, actual.uri_repository);
                Assert.AreEqual(project.user_ownedBy, actual.user_ownedBy);
                Assert.AreEqual(project.uri_projectSpecificMetrics, actual.uri_projectSpecificMetrics);
            }
        }

        [TestMethod()]
        public void ShouldGetPeople()
        {
            Guid pp1 = new Guid("112F9968-8D5E-4d8a-9BD8-9A8DF2342B5A");
            Guid pp2 = new Guid("433F9968-8D5E-4d8a-9BD8-9A8343534B5A");

            using (TransactionScope scope = new TransactionScope())
            {
                CreateFakeProjectStore();

                Dictionary<ProjectPerson, List<Role>> actual = this.store.GetPeople(projectId);

                Assert.IsNotNull(actual);
                Assert.AreEqual(2, actual.Count());
                
                ProjectPerson person1 = actual.Keys.FirstOrDefault(p => p.id == pp1);
                ProjectPerson person2 = actual.Keys.FirstOrDefault(p => p.id == pp2);

                Assert.IsNotNull(person1);
                Assert.AreEqual(projectId, person1.fk_project);
                Assert.IsTrue(person1.isInternal);
                Assert.AreEqual("southy", person1.samAccountName);
                Assert.AreEqual(1, actual[person1].Count());
                Assert.AreEqual("role1", actual[person1].First().name);

                Assert.IsNotNull(person2);
                Assert.AreEqual(projectId, person2.fk_project);
                Assert.IsFalse(person2.isInternal);
                Assert.AreEqual("test", person2.samAccountName);
                Assert.AreEqual(1, actual[person2].Count());
                Assert.AreEqual("role2", actual[person2].First().name);
            }
        }

        [TestMethod()]
        public void ShouldCheckProject()
        {
            string key1 = "All Defects are linked to a Requests";
            string key2 = "All Deliverables are related to a Work Area";
            string key3 = "All Requests are related to a Work Area";
            string key4 = "Has Work Areas created";
            string key5 = "Has People assigned";
            string key6 = "Has Roles assigned";
            string error1 = "The Defect 1 hasn't a request associated";
            string error2 = "The Deliverable deliverable1 hasn't a work area associated";
            string error3 = "The Request 1 hasn't a work area associated";
            string error4 = "The project hasn't Work Areas created";
            using (TransactionScope scope = new TransactionScope())
            {
                CreateFakeProjectStore();

                Dictionary<string, string[]> actual = this.store.CheckProjectCompliance(projectId);

                Assert.IsNotNull(actual);
                Assert.AreEqual(6, actual.Count());

                Assert.IsTrue(actual.Keys.Contains(key1));
                Assert.AreEqual(1, actual[key1].Count());
                Assert.AreEqual(error1, actual[key1][0].Trim());

                Assert.IsTrue(actual.Keys.Contains(key2));
                Assert.AreEqual(1, actual[key2].Count());
                Assert.AreEqual(error2, actual[key2][0].Trim());

                Assert.IsTrue(actual.Keys.Contains(key3));
                Assert.AreEqual(1, actual[key3].Count());
                Assert.AreEqual(error3, actual[key3][0].Trim());

                Assert.IsTrue(actual.Keys.Contains(key4));
                Assert.AreEqual(1, actual[key4].Count());
                Assert.AreEqual(error4, actual[key4][0].Trim());

                Assert.IsTrue(actual.Keys.Contains(key5));
                Assert.IsNull(actual[key5]);
                
                Assert.IsTrue(actual.Keys.Contains(key6));
                Assert.IsNull(actual[key6]);
            }
        }

        [TestMethod()]
        public void ShouldAddPerson()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                using (ScrumLiteDataContext context = new ScrumLiteDataContext())
                {
                    CreateFakeProjectStore();

                    ProjectPerson person = new ProjectPerson() { id = Guid.NewGuid(), fk_project = projectId, isInternal = true, samAccountName = "test" };

                    this.store.AddPerson(person);

                    ProjectPerson actual = (from p in context.ProjectPersons
                                            where p.id == person.id
                                            select p).FirstOrDefault();
                    
                    Assert.IsNotNull(actual);
                    Assert.AreEqual(person.id, actual.id);
                    Assert.AreEqual(person.fk_project, actual.fk_project);
                    Assert.AreEqual(person.isInternal, actual.isInternal);
                    Assert.AreEqual(person.samAccountName, actual.samAccountName);
                }
            }
        }

        [TestMethod()]
        public void ShouldAddPersonRole()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                using (ScrumLiteDataContext context = new ScrumLiteDataContext())
                {
                    CreateFakeProjectStore();

                    ProjectPersonRole personRole = new ProjectPersonRole() { fk_projectPerson = pp2, fk_role = r1Id };

                    this.store.AddPersonRole(personRole);

                    ProjectPersonRole actual = (from p in context.ProjectPersonRoles
                                                where p.fk_projectPerson == pp2 &
                                                p.fk_role == r1Id
                                                select p).FirstOrDefault();

                    Assert.IsNotNull(actual);
                    Assert.AreEqual(personRole.fk_role, actual.fk_role);
                    Assert.AreEqual(personRole.fk_projectPerson, actual.fk_projectPerson);
                }
            }
        }

        [TestMethod()]
        public void ShouldDeletePerson()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                using (ScrumLiteDataContext context = new ScrumLiteDataContext())
                {
                    CreateFakeProjectStore();
                    this.store.RemovePerson(projectId, "southy");

                    ProjectPerson actual = (from p in context.ProjectPersons
                                            where p.id == pp1 &&
                                            p.fk_project == projectId
                                            select p).FirstOrDefault();

                    Assert.IsNull(actual);
                }
            }
        }

        [TestMethod()]
        public void ShouldGetComplianceCheckerProperties()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                CreateFakeProjectStore();

                Dictionary<string, Collection<ComplianceChecker>> properties = this.store.GetComplianceCheckerProperties();

                Assert.IsNotNull(properties);
                Assert.AreEqual(19, properties.Count());
                Assert.IsTrue(properties.Keys.Contains("property1"));
                Assert.AreEqual(1, properties["property1"].Count());
                Assert.AreEqual(DateTime.Now.Date, properties["property1"].First().dateBegin.Date);
                Assert.AreEqual("property1", properties["property1"].First().property);
                Assert.AreEqual("value1", properties["property1"].First().value);
            }
        }

        private static void CreateFakeProjectStore()
        {
            using (ScrumLiteDataContext context = new ScrumLiteDataContext())
            {
                Project project = new Project() { endsOn = DateTime.Today, id = projectId, name = "FakeProject", security_group = "fakeGroup", startsOn = DateTime.Today, uri_repository = "www.fakeUri.com", user_ownedBy = "fakeUser", uri_projectSpecificMetrics = "www.fakeUriMetrics.com" };
                context.Projects.InsertOnSubmit(project);

                ProjectPerson projectPerson1 = new ProjectPerson() { fk_project = projectId, id = pp1, isInternal = true, samAccountName = "southy" };
                ProjectPerson projectPerson2 = new ProjectPerson() { fk_project = projectId, id = pp2, isInternal = false, samAccountName = "test" };
                context.ProjectPersons.InsertOnSubmit(projectPerson1);
                context.ProjectPersons.InsertOnSubmit(projectPerson2);

                Role role1 = new Role() { id = r1Id, name = "role1" };
                Role role2 = new Role() { id = Guid.NewGuid(), name = "role2"};
                context.Roles.InsertOnSubmit(role1);
                context.Roles.InsertOnSubmit(role2);

                ProjectRole projectRole1 = new ProjectRole() { fk_project = projectId, fk_role = role1.id, id = Guid.NewGuid() };
                ProjectRole projectRole2 = new ProjectRole() { fk_project = projectId, fk_role = role2.id, id = Guid.NewGuid() };
                context.ProjectRoles.InsertOnSubmit(projectRole1);
                context.ProjectRoles.InsertOnSubmit(projectRole2);

                ProjectPersonRole projectPersonRole1 = new ProjectPersonRole() { fk_projectPerson = pp1, fk_role = role1.id };
                ProjectPersonRole projectPersonRole2 = new ProjectPersonRole() { fk_projectPerson = pp2, fk_role = role2.id };
                context.ProjectPersonRoles.InsertOnSubmit(projectPersonRole1);
                context.ProjectPersonRoles.InsertOnSubmit(projectPersonRole2);

                Deliverable deliverable1 = new Deliverable() { id = Guid.NewGuid(), pk_project = projectId, releasedOn =DateTime.Now, fileName = "deliverable1", uri_repository = "" };
                context.Deliverables.InsertOnSubmit(deliverable1);

                Defect defect1 = new Defect() { id = Guid.NewGuid(), moniker = 1, fk_deliverable = deliverable1.id, decision = "", createdOn = DateTime.Now, title ="", user_createdBy ="" };
                context.Defects.InsertOnSubmit(defect1);

                Request request1 = new Request() { id = Guid.NewGuid(), moniker = 1, fk_project = projectId, createdOn = DateTime.Now, requestor = "", title = "", committed = false, notes = "" };
                context.Requests.InsertOnSubmit(request1);

                ComplianceChecker projectChecker = new ComplianceChecker { id = Guid.NewGuid(), dateBegin = DateTime.Now, property = "property1", value = "value1" };
                context.ComplianceCheckers.InsertOnSubmit(projectChecker);

                #region Extra Data to probe more scenarios"

                Project project2 = new Project() { endsOn = DateTime.Today, id = Guid.NewGuid(), name = "FakeProject2", security_group = "fakeGroup", startsOn = DateTime.Today, uri_repository = "www.fakeUri.com", user_ownedBy = "fakeUser", uri_projectSpecificMetrics = "www.fakeUriMetrics.com" };
                context.Projects.InsertOnSubmit(project2);

                Deliverable deliverable2 = new Deliverable() { id = Guid.NewGuid(), pk_project = project2.id, releasedOn = DateTime.Now, fileName = "deliverable1", uri_repository = "" };
                context.Deliverables.InsertOnSubmit(deliverable2);

                WorkArea workArea = new WorkArea() { fk_project = project2.id, id = Guid.NewGuid(), name = "test" };
                context.WorkAreas.InsertOnSubmit(workArea);

                DeliverableWorkArea deliverableWorkArea = new DeliverableWorkArea() { id = Guid.NewGuid(), pk_deliverable = deliverable2.id, pk_workarea = workArea.id };
                context.DeliverableWorkAreas.InsertOnSubmit(deliverableWorkArea);

                ProjectPerson projectPerson = new ProjectPerson() { fk_project = project2.id, id = Guid.NewGuid(), samAccountName = "", isInternal = true };
                context.ProjectPersons.InsertOnSubmit(projectPerson);

                Request request2 = new Request() { id = Guid.NewGuid(), moniker = 1, fk_project = project2.id, createdOn = DateTime.Now, requestor = "", title = "", committed = false, notes = "" };
                context.Requests.InsertOnSubmit(request2);

                RequestWorkArea requestWorkArea = new RequestWorkArea() { fk_request = request2.id, fk_workarea = workArea.id, id = Guid.NewGuid() };
                context.RequestWorkAreas.InsertOnSubmit(requestWorkArea);
                #endregion

                context.SubmitChanges();
            }
        }
   }
}
